/*
  (C) 2005 Adam D. Bradley

  This program is free software; you can redistribute it and/or
  modify it under the terms of Version 2 of the GNU General Public 
  License.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package step;

import java.io.OutputStream;
import java.util.ArrayList;

import snbenchlib.Log;
import snbenchlib.Trace;
import snobject.snNil;
import step.exceptions.BackwardIllegalChildException;
import step.exceptions.ForwardIllegalChildException;
import step.exceptions.IllegalChildException;
import step.exceptions.STEPException;

/**
 * A LastTriggerEval node is our roundabout way of allowing "head recursion"
 * in trigger expressions.  It returns the last evaluated value of some
 * parent node, which may be a local trigger or a local socket wired to
 * a remote trigger via a non-blocking read.
 */
public class LastTriggerEval extends ReferenceNode
{
	private String TriggerID;

	public LastTriggerEval(String id, String triggerID) {
		super(id);
		TriggerID = triggerID;
	}

	public String GetTarget() { return TriggerID; }
	
	public void SetTarget(String targetID) { TriggerID = targetID; }
	

	protected void Bind() throws java.lang.ClassNotFoundException {
		SetValue(snNil.NIL);
		//super.wanted = true;
		super.Bind();
	}

	/**
	 * Ensure that this LTE is linked to it's target 
	 */
	protected step.Node Normalize(step.Graph g) throws DanglingSplice {
		if(children.length > 0){
			step.Node T = g.GetNode(TriggerID);
			if(T.GetID().equals(children[0].GetID()))
				return this;
			else{
				System.out.println("Internal consistency error in LTE:Normalize");
				System.exit(-1);
			}
		}
		
		step.Node T = g.GetNode(TriggerID);
		if (T == null)
			throw new DanglingSplice(g, this);
		/**
		 * By doing this, we are creating a cycle in the graph.
		 * This is the ONLY PLACE this should EVER happen!
		 * It only doesn't break because all of our recursive descent
		 * modes (enabled and wanted) are blocked by triggers, and
		 * LTE nodes only have triggers as children.
		 */
		AddChild(T);
		return this;
	}

    public boolean IsEnabled() {
    	if ((!rt_wanted)||(rt_evaluated))
    		return false;
    	
    	/* does an LTE always have fresh input . . ? */
    	return true;
    }

	/** 
	 * Always claim we're a leaf (even though we're not!) 
	 * 
	 * @return
	 */
	public boolean IsLeaf() {
		return true;
	}
    
    protected void Evaluate() throws EvaluationFailure {
	    if(HasFreshInput(0)){
	    	SetValue(GetInput(0));
	    	Log.evaluator.log(
					"LTE "+ super.ID + " used FRESH value " + value.toString(),
					Trace.DEBUG);   	
	    }
	    else{
	    	Log.evaluator.log(
					"LTE "+ super.ID + " using cached value " + value.toString(),
					Trace.DEBUG);
	    }
	    PushResult();
	    rt_evaluated = true;
	}

    /**
     * Some nodes (Triggers, Consts, LTEs) need to override this method to
     * block the "clear" from propogating. 
     */
    protected void ClearEvaluatedFlags() throws EvaluationFailure
    { 
		/* DOME : each "clear" should have a 2-part ID,
		   sender-ID and serial number.  We should reject
		   repeated attempts to clear from the same
		   id:serial pair.  This will fix the nasty
		   problem with letonce instances inside of 
		   persistent triggers. */
    	rt_evaluated = false;
    }
    
    /* Needed to prevent recursing into children... */
    public final void WriteBasis(OutputStream out, int depth, String attrs) throws java.io.IOException
    {
    	
    	Indent(out, depth);
    	out.write(("<" + TagName() + " id=\"" + ID + "\" target=\"" + TriggerID + "\"").getBytes());
    	if ((attrs!=null) && (attrs.length()>0)) {
    		out.write((" " + attrs).getBytes());
    	}
    	if (types.size() > 0) {
    		out.write(">\n".getBytes());
    		for (FlowType i: types.values()) {
    			i.Write(out, depth+1);
    		}
    		Indent(out, depth);
    		out.write(("</" + TagName() + ">\n").getBytes());
    	} else {
    		out.write("/>\n".getBytes());
    	}
    }
    
	/* (non-Javadoc)
	 * @see step.Node#TagName()
	 */
	public String TagName() {
		return "last_trigger_eval";
	}
	
	// this code is added by Ray

	//	 MODIFIES: compilationErrors 
	// EFFECT: checks if an LTE node has any children or its target is not a Trigger it adds exceptions to compilationErrors 
	// 
	// also if it found that the "Target" is not a direct ancestor of this LTE it adds exceptions to compilationErrors

	public void validate(step.Graph g,
			ArrayList<STEPException> compilationErrors) {
		// no child check
		if (children.length != 0) {
			compilationErrors
					.add(new ForwardIllegalChildException(
							"Node "
									+ this.GetID()
									+ " :this LTE has children: LTE must link to a Trigger and have no Children"));
		}
		// mismatch tag check
		step.Node T = g.GetNode(TriggerID);
		if (T == null || !T.GetID().equals(TriggerID)) {
			compilationErrors
					.add(new IllegalChildException(
							"Node "
									+ this.GetID()
									+ " :the refrenced trigger doesn't have the same name as the trigger id stored here"));
			return;
		}
		// check that the referenced node is a trigger
		if (!(T.TagName().equals("trigger")
				|| T.TagName().equals("edge_trigger") || T.TagName().equals(
				"level_trigger"))) // ask about adding TriggerRead Node
			compilationErrors.add(new IllegalChildException("Node "
					+ this.GetID() + " :this LTE refrence to node of type"
					+ T.TagName() + " :LTE must link to a Trigger"));
		// check that all paths of parents leads to the refernced trigger
		
		if(!this.GetAllAncestorsCycleSafe().containsKey(TriggerID)){
			compilationErrors
					.add(new BackwardIllegalChildException(
							"Node "
									+ this.GetID()
									+ " :this LTE is not a descendant of its refernced trigger (may be erroneous if SpliceNodes are present"));
		}
	}

	// added by jching
	// 	MODIFIES: outputStream
	// EFFECT: it write the syntax of LTE node and thraws exception if something gone wrong
	public void Write(OutputStream out, int depth) throws java.io.IOException {
		Indent(out, depth);
		out.write(("<" + TagName() + " id=\"" + ID + "\" " + "target=\""
				+ TriggerID + "\"/>\n").getBytes());
	}
	
	@Override
	public Node ShallowCopy() {
		LastTriggerEval cn = new LastTriggerEval(this.ID, this.TriggerID);
		Node.CopyNonRTPropertiesFromAtoB(this,cn);
		return cn;
	}
	
}